<template>
  <transition :name="animation">
    <div class="nut-calendar" v-show="childIsVisible">
      <div class="nut-calendar-control">
        <span
          class="nut-calendar-confirm-btn"
          @click="confirm"
          v-if="(type == 'range' && currDate && currDate.length == 2) || type != 'range'"
        >确 定</span>
        <span class="nut-calendar-cancel-btn" @click="closeActionSheet">取 消</span>
        <div class="nut-calendar-title">{{title || '选择日期'}}</div>
        <div class="nut-calendar-week">
          <span v-for="(item, index) of week" :key="index">{{item}}</span>
        </div>
      </div>
      <div
        class="nut-calendar-months"
        @touchstart.stop="touchStart"
        @touchmove.stop.prevent="touchMove"
        @touchend.stop="touchEnd"
      >
        <div
          class="nut-calendar-months-panel"
          ref="months"
          :style="{ transform: translateY, transition: transition }"
        >
          <div class="nut-calendar-loading-tip">{{!unLoadPrev ? '加载上一个月' : '没有更早月份~'}}</div>
          <div class="nut-calendar-month" v-for="(month, index) of monthsData " :key="index">
            <!-- 每一个月的标题 如 2020年06月 -->
            <div
              class="nut-calendar-month-title"
              :style="`height:${lineHeight}rpx;line-height:${lineHeight}rpx;`"
            >{{month.title}}</div>
            <div class="nut-calendar-month-con">
              <div class="nut-calendar-month-item">
                <template v-for="(day, i) of  month.monthData">
                  <div
                    class="nut-calendar-month-day"
                    :key="i"
                    @click="chooseDay(day, month)"
                    :style="dayStyle"
                  >
                    <span>{{day.type == 'curr' ? day.day : ''}}</span>
                    <span class="nut-calendar-day-tip" v-if="isStartTip(day, month)">开始</span>
                    <span class="nut-calendar-day-tip" v-else-if="isEndTip(day, month)">结束</span>
                  </div>
                </template>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </transition>
</template>
<script>
import * as Utils from "@/utils/date.js";
export default {
  name: "nut-calendar",
  props: {
    type: {
      type: String,
      default: "one"
    },
    animation: {
      type: String,
      default: "nutSlideUp"
    },
    isAutoBackFill: {
      type: Boolean,
      default: false
    },
    isOpenRangeSelect: {
      type: Boolean,
      default: false
    },
    isVisible: {
      type: Boolean,
      default: false
    },
    title: {
      type: String
    },
    defaultValue: {
      type: String | Array,
      default: null
    },
    startDate: {
      type: String
      //default: null
      // default: Utils.getDay(0)
    },
    endDate: {
      type: String
      //default: null
      // default: Utils.getDay(365)
    },
    lineHeight: {
      type: Number,
      default: 100
    }
  },
  watch: {
    isVisible: {
      immediate: true,
      handler(newValue) {
        this.childIsVisible = newValue;
      }
    }
  },
  data() {
    const week = "日,一,二,三,四,五,六";
    return {
      clientHeight: 0,
      childIsVisible: false,
      currDate: null,
      week: week.split(","),
      unLoadPrev: false,
      unLoadNext: false,
      touchParams: {
        startY: 0,
        endY: 0,
        startTime: 0,
        endTime: 0
      },
      transformY: 0,
      scrollDistance: 0,
      defaultData: null,
      startData: this.startDate ? this.splitDate(this.startDate) : null,
      endData: this.endDate ? this.splitDate(this.endDate) : null,
      chooseData: [],
      monthsData: [],
      dayPrefix: "nut-calendar-month-day"
    };
  },
  computed: {
    isRange: function() {
      return this.type === "range";
    },
    // 过渡
    transition() {
      return `transform 300ms cubic-bezier(0.19, 1, 0.22, 1)`;
    },
    translateY() {
      return `translateY(${this.scrollDistance}px)`; // transform会使fixed失效,需注意把fixed元素写在mescroll之外
    },

    dayStyle() {
      return `height : ${this.lineHeight}rpx`;
    }
  },
  methods: {
    isActive(day, month) {
      return (
        this.isRange &&
        day.type == "curr" &&
        this.getClass(day, month) == "nut-calendar-month-day-active"
      );
    },

    isStartTip(day, month) {
      if (this.isActive(day, month)) {
        return this.isStart(this.getCurrDate(day, month));
      } else {
        return false;
      }
    },

    isEndTip(day, month) {
      return this.isActive(day, month);
    },

    getCurrData(type) {
      let monthData =
        type == "prev"
          ? this.monthsData[0]
          : this.monthsData[this.monthsData.length - 1];
      let year = parseInt(monthData.curData[0]);
      let month = parseInt(monthData.curData[1].toString().replace(/^0/, ""));
      switch (type) {
        case "prev":
          month == 1 && (year -= 1);
          month = month == 1 ? 12 : --month;
          break;
        case "next":
          month == 12 && (year += 1);
          month = month == 12 ? 1 : ++month;
          break;
      }
      return [year, Utils.getNumTwoBit(month), monthData.curData[2]];
    },

    getDaysStatus(days, type) {
      // 修复：当某个月的1号是周日时，月份下方会空出来一行
      if (type == "prev" && days >= 7) {
        days -= 7;
      }
      return Array.from(Array(days), (v, k) => {
        return {
          day: k + 1,
          type: type
        };
      });
    },

    getMonth(curData, type) {
      let preMonthDays = Utils.getMonthPreDay(curData[0], curData[1]);
      let currMonthDays = Utils.getMonthDays(curData[0], curData[1]);
      let nextMonthDays = 42 - preMonthDays - currMonthDays;
      let title = `${curData[0]}年${curData[1]}月`;
      let monthInfo = {
        curData: curData,
        title: title,
        monthData: [
          ...this.getDaysStatus(preMonthDays, "prev"),
          ...this.getDaysStatus(currMonthDays, "curr")
        ]
      };
      if (type == "next") {
        if (
          !this.endData ||
          !Utils.compareDate(
            `${this.endData[0]}-${this.endData[1]}-${Utils.getMonthDays(
              this.endData[0],
              this.endData[1]
            )}`,
            `${curData[0]}-${curData[1]}-${curData[2]}`
          )
        ) {
          this.monthsData.push(monthInfo);
        } else {
          this.unLoadNext = true;
        }
      } else {
        if (
          !this.startData ||
          !Utils.compareDate(
            `${curData[0]}-${curData[1]}-${curData[2]}`,
            `${this.startData[0]}-${this.startData[1]}-01`
          )
        ) {
          this.monthsData.unshift(monthInfo);
        } else {
          this.unLoadPrev = true;
        }
      }
    },

    getCurrDate(day, month, isRange) {
      return isRange
        ? month.curData[3] +
            "-" +
            month.curData[4] +
            "-" +
            Utils.getNumTwoBit(day.day)
        : month.curData[0] +
            "-" +
            month.curData[1] +
            "-" +
            Utils.getNumTwoBit(day.day);
    },

    isStart(currDate) {
      return Utils.isEqual(this.currDate[0], currDate);
    },

    isEnd(currDate) {
      return Utils.isEqual(this.currDate[1], currDate);
    },

    splitDate(date) {
      return date.split("-");
    },

    getClass(day, month, isRange) {
      let currDate = this.getCurrDate(day, month, isRange);
      if (day.type == "curr") {
        if (
          (!this.isRange && Utils.isEqual(this.currDate, currDate)) ||
          (this.isRange && (this.isStart(currDate) || this.isEnd(currDate)))
        ) {
          return `${this.dayPrefix}-active`;
        } else if (
          (this.startDate && Utils.compareDate(currDate, this.startDate)) ||
          (this.endDate && Utils.compareDate(this.endDate, currDate))
        ) {
          return `${this.dayPrefix}-disabled`;
        } else if (
          this.isRange & (this.currDate.length == 2) &&
          Utils.compareDate(this.currDate[0], currDate) &&
          Utils.compareDate(currDate, this.currDate[1])
        ) {
          return `${this.dayPrefix}-choose`;
        } else {
          return null;
        }
      } else {
        return `${this.dayPrefix}-disabled`;
      }
    },

    chooseDay(day, month, isFirst, isRange) {
      if (this.getClass(day, month, isRange) != `${this.dayPrefix}-disabled`) {
        let days = [...month.curData];
        days = isRange ? days.splice(3) : days.splice(0, 3);
        days[2] =
          typeof day.day == "number" ? Utils.getNumTwoBit(day.day) : day.day;
        days[3] = `${days[0]}-${days[1]}-${days[2]}`;
        days[4] = Utils.getWhatDay(days[0], days[1], days[2]);
        if (!this.isRange) {
          this.currDate = days[3];
          this.chooseData = [...days];
        } else {
          if (this.currDate.length == 2) {
            this.currDate = [days[3]];
          } else {
            if (Utils.compareDate(this.currDate[0], days[3])) {
              this.currDate.push(days[3]);
            } else {
              this.currDate.unshift(days[3]);
            }
          }
          if (this.chooseData.length == 2 || !this.chooseData.length) {
            this.chooseData = [...days];
          } else {
            if (Utils.compareDate(this.chooseData[3], days[3])) {
              this.chooseData = [[...this.chooseData], [...days]];
            } else {
              this.chooseData = [[...days], [...this.chooseData]];
            }
          }
        }
        if (this.isAutoBackFill && !isFirst) {
          this.confirm();
        }
      }
    },

    confirm() {
      if ((this.isRange && this.chooseData.length == 2) || !this.isRange) {
        this.$emit("choose", this.chooseData);
        this.childIsVisible = false;
        this.$emit("update:is-visible", false);
      }
    },

    resetRender() {
      this.chooseData.splice(0);
      this.monthsData.splice(0);
      this.scrollDistance = 0;
      this.translateY = 0;
      this.setTransform(this.scrollDistance);
      this.initData();
    },

    closeActionSheet() {
      this.childIsVisible = false;
      this.$emit("update:is-visible", false);
      this.$emit("close");
      this.resetRender();
    },

    touchStart(event) {
      let changedTouches = event.changedTouches[0];
      this.touchParams.startY = changedTouches.pageY;
      this.touchParams.startTime = event.timestamp || new Date().getTime();
      this.transformY = this.scrollDistance;
    },

    touchMove(event) {
      debugger;
      // #ifdef H5
      window.isPreventDefault = false; // 标记不需要阻止window事件
      // #endif
      if (!this.touchParams.startY) return;
      let me = this;

      // 节流
      me.touchParams.lastTime = event.timestamp || new Date().getTime();
      if (me.touchParams.lastTime - me.touchParams.startTime < 20) {
        // 小于节流时间,则不处理
        return;
      } else {
        me.touchParams.startTime = me.touchParams.lastTime;
        // if (!me.moveTimeDiff) me.moveTimeDiff = 1000 / me.optDown.fps;
      }
      // 获取移动中手指的 Y轴位置
      let changedTouches = event.changedTouches[0];
      this.touchParams.lastY = changedTouches.pageY;

      // 获取手指Y轴方向移动的距离
      let moveY = this.touchParams.lastY - this.touchParams.startY;
      if (Math.abs(moveY) < 5) {
        return false;
      }
      console.log(`moveY = ${moveY}`);

      this.setMove(moveY);
    },

    touchEnd(event) {
      let changedTouches = event.changedTouches[0];
      this.touchParams.lastY = changedTouches.pageY;
      this.touchParams.lastTime = event.timestamp || Date.now();
      let move = this.touchParams.lastY - this.touchParams.startY;
      if (Math.abs(move) < 5) {
        return false;
      }
      let updateMove = move + this.transformY;
      // let h =
      //   window.innerHeight ||
      //   document.documentElement.clientHeight ||
      //   document.body.clientHeight;
      let offsetHeight = 0;
      const query = uni.createSelectorQuery().in(this);
      query
        .select(".nut-calendar-months-panel")
        .boundingClientRect(data => {
          console.log("得到布局位置信息" + JSON.stringify(data));
          console.log("节点离页面顶部的距离为" + data.top);
          offsetHeight = data.offsetHeight;
        })
        .exec();

      // let offsetHeight = this.$refs.months.offsetHeight;
      if (updateMove > 0) {
        this.getMonth(this.getCurrData("prev"), "prev");
      } else if (updateMove < -offsetHeight + this.clientHeight * 2) {
        this.getMonth(this.getCurrData("next"), "next");
        if (Math.abs(move) >= 300) {
          this.getMonth(this.getCurrData("next"), "next");
        }
      }

      let moveTime = this.touchParams.lastTime - this.touchParams.startTime;
      if (moveTime <= 300) {
        move = move * 2;
        moveTime = moveTime + 1000;
        this.setMove(move, "end", moveTime);
      } else {
        this.setMove(move, "end");
      }
    },

    setTransform(translateY = 0, type, time = 1000) {
      // if (type === "end") {
      //   this.$refs.months.style.webkitTransition = `transform ${time}ms cubic-bezier(0.19, 1, 0.22, 1)`;
      //   //this.$refs.months.style.transition = `transform ${time}ms cubic-bezier(0.19, 1, 0.22, 1)`;
      // } else {
      //   this.$refs.months.style.webkitTransition = "";
      //   //this.$refs.months.style.transition = '';
      // }

      this.scrollDistance = translateY;
    },

    /**
     * 设置滑动
     *
     * @param move 移动的Y轴方向移动的距离 可能为负值
     * @param type 当前移动的类型 'end'为手指放开
     * @param time
     */
    setMove(move, type, time) {
      // 获取当前移动的真正 距离
      let updateMove = move + this.transformY;
      let h = this.clientHeight;
      // 一天的高度
      let dayHeight = this._getDayHeight();
      let offsetHeight = 0;
      const query = uni.createSelectorQuery().in(this);
      query
        .select(".nut-calendar-months-panel")
        .boundingClientRect(data => {
          console.log("得到布局位置信息" + JSON.stringify(data));
          console.log("offsetHeight = " + data.height);
          console.log(" h = " + h);
          offsetHeight = data.height;
        })
        .exec();
      if (type === "end") {
        // 限定滚动距离
        // if (updateMove > 0) {
        //   updateMove = 0;
        // }
        // if (updateMove < 0 && updateMove < -offsetHeight + h - 70) {
        //   updateMove = -offsetHeight + h - 70;
        // }
        // if (offsetHeight <= h && this.monthsData.length == 1) {
        //   updateMove = 0;
        // }
        let endMove = updateMove;
        this.setTransform(endMove, type, time);
      } else {
        // 如果下拉 超过一个星期那么就加载一个星期
        if (updateMove > 0 && updateMove > dayHeight) {
          updateMove = dayHeight;
        }
        // 如果超过最大日期 那么就只能下拉那么长的距离
        if (
          updateMove < -offsetHeight + h - 170 &&
          this.monthsData.length > 1
        ) {
          updateMove = -offsetHeight + h - 170;
        }
        // 如果上拉显示下一个星期的，超过一个星期的长度 那么就上滑 一个一行
        if (
          updateMove < 0 &&
          updateMove < -dayHeight &&
          this.monthsData.length == 1
        ) {
          updateMove = -dayHeight;
        }
        this.setTransform(updateMove);
      }
    },

    initData() {
      if (!this.defaultValue) {
        this.currDate = this.isRange
          ? [Utils.date2Str(new Date()), Utils.getDay(2)]
          : Utils.date2Str(new Date());
      } else {
        this.currDate = this.isRange
          ? [...this.defaultValue]
          : this.defaultValue;
      }

      if (this.isRange) {
        if (
          this.startDate &&
          Utils.compareDate(this.currDate[0], this.startDate)
        ) {
          this.currDate.splice(0, 1, this.startDate);
        }
        if (this.endDate && Utils.compareDate(this.endDate, this.currDate[1])) {
          this.currDate.splice(1, 1, this.endDate);
        }
        this.defaultData = [
          ...this.splitDate(this.currDate[0]),
          ...this.splitDate(this.currDate[1])
        ];
      } else {
        if (
          this.startDate &&
          Utils.compareDate(this.currDate, this.startDate)
        ) {
          this.currDate = this.startDate;
        } else if (
          this.endDate &&
          !Utils.compareDate(this.currDate, this.endDate)
        ) {
          this.currDate = this.endDate;
        }
        this.defaultData = [...this.splitDate(this.currDate)];
      }

      this.getMonth(this.defaultData, "next");

      let i = 1;
      do {
        this.getMonth(this.getCurrData("next"), "next");
      } while (i++ < 4);

      if (this.isRange) {
        this.chooseDay(
          { day: this.defaultData[2], type: "curr" },
          this.monthsData[0],
          true
        );
        this.chooseDay(
          { day: this.defaultData[5], type: "curr" },
          this.monthsData[0],
          true,
          true
        );
      } else {
        this.chooseDay(
          { day: this.defaultData[2], type: "curr" },
          this.monthsData[0],
          true
        );
      }
    },

    _getDayHeight() {
      return uni.upx2px(this.lineHeight + "rpx");
    }
  },

  mounted() {
    let _this = this;
    uni.getSystemInfo({
      success(res) {
        _this.clientHeight = res.windowHeight;
      }
    });

    this.initData();
  }
};
</script>
<style lang="scss" src="./index.scss">
</style>